--[[
https://github.com/yongkangchen/poker-server

Copyright (C) 2016  Yongkang Chen lx1988cyk#gmail.com

GNU GENERAL PUBLIC LICENSE
   	Version 3, 29 June 2007

Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
--]]

local do_check_hu = require "hu"
local do_check_hu_type = require "hu_type"
local msg = require "msg"
local log = require "log"
local LERR = log.error
local LLOG = log.log

local HORSE_TBL = {
    [0] = true,
    [2] = true,
    [4] = true,
    [6] = true,
}

local function remove_card(tbl, value, count)
    count = count or 1
    for i = #tbl, 1, -1 do
        if tbl[i] == value then
            table.remove(tbl, i)
            count = count - 1
            if count == 0 then
                return
            end
        end
    end
end

local function get_next_idx(idx)
    idx = idx + 1
    return idx == 5 and 1 or idx
end

local band = bit.band
local rshift = bit.rshift

local function check_dict_hu(dict, seven_hu)
    local results = do_check_hu(dict)
    if results and #results == 1 and results[1] == 0x4000000 and not seven_hu then
        results = nil
    end
    return results
end

local function check_hu(room, cards, card)
    local dict = {}
    
    for _, v in ipairs(cards) do
        dict[v] = (dict[v] or 0) + 1
    end
    
    if card then
        dict[card] = (dict[card] or 0) + 1
    end
    
    local results = check_dict_hu(dict, room.seven_hu)
    if not results then
        return
    end
    
    if results[1] == 0x4000000 then
        return 2
    end
    
    if room.peng_hu then
        for _, result in ipairs(results) do
            local shunzi = band(rshift(result, 3), 0x7)
            if shunzi == 0 then
                return 2
            end
        end
    end
    return 1
end

local function card_count(tbl, value)
    local count = 0
    for _, v in ipairs(tbl) do
        if v == value then
            count = count + 1
        end
    end
    return count
end


local function create_info(result)
    return {
        result = result or {
            total_score = 0,
            win_count = 0, 
            gang_count = 0, 
            an_gang_count = 0, 
            fang_gang_count = 0,
            horse_count = 0, 
            pao_count = 0
        },
        score = 0,
        is_huaz = false,
        is_ready = false,
        gsh_cardv = 200,
        hu_type_gsh = 0,
        is_out = 0,
        add_hand = {},
        next_card = -1,
    }
end

local start_room,check_master
local function set_init_msg(player, data)
    local player_info = player.info
    data.extra = player_info.extra
    data.new_idx = player_info.new_idx
    data.can_out = player.room.can_out and player.room.players[player.room.idx] == player
    data.pre_out_role = player.room.pre_out_role == player
end

--[[
--local function create_room(player, type, horse, peng_hu, seven_hu, can_pao, horse_type)
local function create_room(player, type, jf_jd, fan_num, gex_tbl)
    LLOG("+++create_room type: %s jf_jd: %d fan_num: %d gex_tbl: %s", type, jf_jd, fan_num, table.dump(gex_tbl))
    
    return {
        player_size = 4,
        idx = 1,
        zhuang = nil,
        
        horse = horse or 0,
        horse_type = horse_type or 0,
        peng_hu = peng_hu or 0,
        seven_hu = seven_hu or 0,
        can_pao = can_pao or 0,
        create_data = {
            horse = horse,
            seven_hu = seven_hu,
            type = type,
            can_pao = can_pao,
            qianggang_hu = true,
        },
        start_idx = 1,
        can_out = false,
        type = type,
    }
end
]]


local function create_room(player, type, jf_jd, fan_num, gex_tbl)
    LLOG("+++create_room type: %s jf_jd: %d fan_num: %d gex_tbl: %s", type, jf_jd, fan_num, table.dump(gex_tbl))
    
    return {
        player_size = 4,
        idx = 1,
        zhuang = nil,
        
        jf_jd = jf_jd,
        fan_num = fan_num,
        peng_hu = 0,
        gex_tbl = gex_tbl,
        create_data = {
            jf_jd = jf_jd,
            fan_num = fan_num,
            peng_hu = 0,
            gex_tbl = gex_tbl,
            type = type,
            qianggang_hu = true,
        },
        start_idx = 1,
        can_out = false,
        exg_3cards_tbl = {},
        que_idx_tbl = {},
        exg_3cards_idx_tbl = {},
        dajiao_dict = {},
        weijiao_dict = {},
        sz_val = {},
        hu_tbl = {},
        hu_card = {},
        seven_hu = true,
        is_called_end = false,
        type = type,
    }
end

local function renter(_, player)
    for name in pairs(player.info.select or {}) do
        player:send(msg.SELECT, name)
    end
end

local function add_gang_score(player, player_dict, room)
    for _, v in ipairs(player.info.extra or {}) do
        if v.num == 4 then
            if v.pid then
                local out_player = player_dict[v.pid]
                out_player.info.score = out_player.info.score - 2
                player.info.score = player.info.score + 2
            else
                local add_score = v.gong and 1 or 2
                local add_ct = 0
                for _, role in pairs(player_dict) do
                    if role ~= player and not room.hu_card[role.id] then
                        role.info.score = role.info.score - add_score
                        add_ct = add_ct+ 1
                    end
                end
                player.info.score = player.info.score + add_score * add_ct
            end
        end
    end
end


local function add_huazhu( player_dict )
    local hua_zhu_dict = {}
    local type_dict = {}
    for _,role in pairs(player_dict) do
        type_dict = {}
        for _,v in pairs(role.info.hand) do
            local tp = get_card_type(v)
            type_dict[tp] = 1
            if table.length( type_dict ) == 3 then
                role.info.is_huaz = true
                hua_zhu_dict[ role.id ] = 1
                --table.insert(hua_zhu_tbl, role.id)
                break
            end
        end
    end
    if table.length(hua_zhu_dict) == 4 then
        return
    end

    local no_huaz_count = 4 - table.length( hua_zhu_dict )
    for _,role in pairs(player_dict) do
        if role.info.is_huaz then
            role.info.score = role.info.score - 16*no_huaz_count
        else
            role.info.score = role.info.score + 16*table.length(hua_zhu_dict)
        end
    end
end

local function get_near_cards( val )
    local tp = get_card_type( val )
    local ret_lst = {}
    if get_card_type(val-1) == tp then
        table.insert(ret_lst, val-1)
    end
    if get_card_type(val+1) == tp then
        table.insert(ret_lst, val+1)
    end
    table.insert( ret_lst, val )
    return ret_lst
    -- body
end

local function get_hu_list( hand,room )
    local hu_dic = {}
    local may_hu_dic = {}

    for k,v in pairs(hand) do
        local next_lst = get_near_cards(v)
        for _,val in pairs(next_lst) do
            may_hu_dic[val] = 1
        end
    end

    for val,_ in pairs(may_hu_dic) do
        local res = check_hu(room,hand,val)
        if res then
            hu_dic[val] = 1
        end
    end
    return hu_dic
end

local function get_max_tbl_val(tbl)
    local max_val = -1
    for _,v in pairs(tbl) do
        if v > max_val then
            max_val = v
        end
    end
    return max_val
end

local function process_dajiao( player_dict, room)
    room.weijiao_dict = {}
    room.dajiao_dict = {}
    for _,role in pairs(player_dict) do
        if not room.hu_tbl[role.id] and not is_player_que(room, role) then

            local hu_dic = get_hu_list(role.info.hand, room)
            LLOG("hu_dic: %s", table.dump( hu_dic) )
            if table.length( hu_dic ) ~= 0 then
                room.dajiao_dict[role.id] = 1
                role.info.is_dajiao = true
            else
                room.weijiao_dict[role.id] = 1
                role.info.is_dajiao = false
            end
        end
    end

    local dajiao_len = table.length( room.dajiao_dict )
    local weijiao_len = table.length( room.weijiao_dict )
    --return back weijiao gang
    if dajiao_len == 0 or weijiao_len == 0 then
        return
    end
    for dajiao_id,_ in pairs( room.dajiao_dict ) do
        local dajiao_role = player_dict[dajiao_id]
        for weijiao_id,_ in pairs(room.weijiao_dict) do
            local weijiao_role = player_dict[weijiao_id]
            dajiao_role.info.score = dajiao_role.info.score +1
            weijiao_role.info.score = weijiao_role.info.score - 1
        end
    end
end

local function process_buchajia( player_dict, room )
    if not room.gex_tbl[3] then
        return
    end
    local ting_len = table.length( room.dajiao_dict)
    if ting_len < 2 then
        return
    end
    local role_hurole_dict = {}
    local role_hu_dict = {}

    local hu_lst = {}
    for id, _ in pairs(room.dajiao_dict) do
        local role = room:get_player_by_pid(id)
        local hu_max_v = get_max_tbl_val( get_hu_list(role.info.hand, room) )
        table.insert(hu_lst, hu_max_v)
        role_hu_dict[role.id] = hu_max_v
        if role_hurole_dict[hu_max_v] then
            table.insert(role_hurole_dict[hu_max_v], role)
        else
            role_hurole_dict[hu_max_v] = {role}
        end
    end

    table.sort(hu_lst)
    local res_tbl = {}
    for i=1,#hu_lst-1 do
        local now_v = hu_lst[i]
        local now_role_tbl = role_hurole_dict[now_v]
        for j=i+1,#hu_lst-1 do
            local now_jv = hu_lst[j]
            local now_jrole_tbl = role_hurole_dict[now_jv]
            local big_val = get_card_type_val(now_jv)
            local small_val = get_card_type_val(now_v)            
            local diff_val = big_val - small_val
            for _,role in pairs(now_role_tbl) do
                role.info.score = role.info.score - diff_val*#now_jrole_tbl
            end
            for _,role in pairs(now_jrole_tbl) do
                role.info.score = role.info.score + diff_val*#now_role_tbl
            end
        end
    end

end

BASE_HU_TYPE = {
    HU_ZIMO = 1,
    HU_GSH = 2,
    HU_GSP = 3,
    HU_QGH = 4,
    HU_TIANHU = 5,
    HU_DIHU = 6,
    HU_HU = 7,
}

BASE_HU_FAN = {
    [0] = 0,
    [BASE_HU_TYPE.HU_ZIMO] = 1,
    [BASE_HU_TYPE.HU_GSH] = 2,
    [BASE_HU_TYPE.HU_GSP] = 3,
    [BASE_HU_TYPE.HU_QGH] = 4,
    [BASE_HU_TYPE.HU_TIANHU] = 5,
    [BASE_HU_TYPE.HU_DIHU] = 6,
    [BASE_HU_TYPE.HU_HU] = 1,
}
--总计分 = 胡牌计分 + 开杠计分 + 查牌计分 + 钓鱼得分                   

local function get_result(room, is_over)
    local horse_tbl = room.horse_tbl
    room.horse_tbl = nil
    
    local player_dict = {}
    for _, role in ipairs(room.players) do
        player_dict[role.id] = role
        role.info.score = 0
    end
    
    --胡牌计分
    --胡牌方式
    local base_hu_fan_tbl = {}
    local base_hu_type_tbl = {}
    for _, role in ipairs(room.players) do
        if room.hu_tbl[role.id] then
            LLOG("rid: %d hu_type: %d, hu_type_gsh: %d hu_type_gsp: %d zimo: %d hu_tbl: %s", role.id, role.info.hu_type or -100, 
                role.info.hu_type_gsh or -100,role.info.hu_type_gsp or -100, role.info.zimo or -100, table.dump(room.hu_tbl))
            if role.info.hu_type == BASE_HU_TYPE.HU_TIANHU or role.info.hu_type == BASE_HU_TYPE.HU_DIHU then
                base_hu_type_tbl[role.id] = role.info.hu_type            
            elseif role.info.hu_type_gsh ~= 0 then
                base_hu_type_tbl[role.id] = BASE_HU_TYPE.HU_GSH
                base_hu_fan_tbl[role.id] = BASE_HU_FAN[base_hu_type_tbl[role.id]]
            elseif role.info.hu_type_gsp ~= 0 then
                base_hu_type_tbl[role.id] = BASE_HU_TYPE.HU_GSP
            elseif role.info.zimo and role.info.zimo > 0 then
                base_hu_type_tbl[role.id] = BASE_HU_TYPE.HU_ZIMO
            else
                base_hu_type_tbl[role.id] = BASE_HU_TYPE.HU_HU
            end
        else
            base_hu_type_tbl[role.id] = 0
        end

        base_hu_fan_tbl[role.id] = BASE_HU_FAN[base_hu_type_tbl[role.id]]
    end

    local dahu_type_tbl = {}
    local dahu_fan_tbl = {}
    for _,role in pairs(room.players) do
        if base_hu_type_tbl[role.id] ~= 0 then
            local dahu_tp = do_check_hu_type.get_hu_type(role.info.hand, role.info.extra)
            LLOG("role id: %d base_hu_type_tbl: %s dahu_tp: %d hand: %s extra: %s", role.id, table.dump(base_hu_type_tbl), dahu_tp, table.dump(role.info.hand ), table.dump(role.info.extra) )
            dahu_type_tbl[role.id] = dahu_tp

            dahu_fan_tbl[role.id] = do_check_hu_type.get_hu_fan(role.info.hand, dahu_tp)
        else
            dahu_type_tbl[role.id] = 0
            dahu_fan_tbl[role.id] = 0
        end
    end

    LLOG("dahu_fan_tbl: %s, dahu_fan_tbl: %s", table.dump( dahu_type_tbl), table.dump(dahu_fan_tbl) )

    local total_fan_tbl = {}
    local sub_score_tbl = {}
    for _,role in pairs(room.players) do
        if room.hu_card[role.id] then
            local now_fan = base_hu_fan_tbl[role.id] + dahu_fan_tbl[role.id]
            if now_fan > room.fan_num then
                now_fan = room.fan_num
            end
            total_fan_tbl[role.id] = now_fan
            local now_score = math.pow(2, now_fan)
            local hu_p_tbl = room.hu_card[role.id][2]

            for _,p in pairs(hu_p_tbl) do
                LLOG("pid: %d -score: %d", p.id, now_score)
                p.info.score = p.info.score - now_score
            end
            --当前有多少aliva
            role.info.score = role.info.score + #hu_p_tbl * now_score
            LLOG("pid: %d now score: %d", role.id, role.info.score)
        end
        --i room.hu_card[role.id]
    end

    for _,role in ipairs(room.players) do
        LLOG("before huazhu pid: %d score: %d", role.id, role.info.score)
    end

    for _, role in ipairs(room.players) do
        --if not room.weijiao_dict[role.id] then
        add_gang_score(role, player_dict, room)
        --end
    end

    for _,role in ipairs(room.players) do
        LLOG("before huazhu pid: %d score: %d", role.id, role.info.score)
    end

    add_huazhu( player_dict)
    process_dajiao( player_dict, room )

    process_buchajia(player_dict, room)
    
    local result = {}
    for idx, role in ipairs(room.players) do
        role.info.result.total_score = role.info.result.total_score + role.info.score
        local now_hu_card = -1
        local is_ting = true
        if room.hu_card[role.id] then
            now_hu_card = room.hu_card[role.id][1]
            is_ting = false
        else
            if table.length( get_hu_list(role.info.hand, room ) ) ~= 0 then
                is_ting = true
            else
                is_ting = false
            end
            --is ting pai
        end

        LLOG("fish_res_tbl : %s", table.dump(room.fish_res_tbl))
        if room.flag_fish then
            role.fish_card = room.fish_res_tbl[ role.id ] or -1
        end
        local tbl =  {
            name = role.name,
            id = role.id,
            headimgurl = role.headimgurl,
            
            is_host = role == room.host,
            is_zhuang = room.zhuang == role,
            extra = role.info.extra,
            
            hand = role.info.hand,
            add_score = role.info.score,

            fish_card = role.fish_card,
            is_ting = is_ting,
            is_huazhu = role.info.is_huaz or false,
            is_dajiao = role.info.is_dajiao or false,
            ming_gang_ct = role.info.result.gang_count or 0,
            an_gang_ct = role.info.result.an_gang_count or 0,
            
            is_pao = role.info.is_pao,
            zimo = role.info.zimo,
            base_hu_type = base_hu_type_tbl[role.id],
            dahu_type_tbl = dahu_type_tbl[role.id],
            hu_card = now_hu_card,
            fan_num = total_fan_tbl[role.id] or 0,
            --horse_tbl = role.info.horse_tbl,
            result = is_over and role.info.result or nil,
        }
        LLOG("id: %d extra: %s hand: %s add_score: %s is_pao: %s zimo: %s base_hu_type: %s dahu_type_tbl: %s hu_card: %s",
            role.id, table.dump(role.info.extra), table.dump(role.info.hand), tostring(role.info.score), 
            tostring(role.info.is_pao), tostring(role.info.zimo), table.dump(base_hu_type), table.dump(dahu_type_tbl), 
            tostring(now_hu_card) )
        table.insert(result, tbl)
        LLOG("get_result tbl: %s", table.dump(tbl) )
        role.info = create_info(role.info.result)
    end
    if room.next_start_idx >=1 and room.next_start_idx<=4 then
        local next_zh_p = room.players[room.next_start_idx]
        result.next_zhuang_id = next_zh_p.id
    else
        result.next_zhuang_id = -1
    end

    result.horse = horse_tbl
    room.hu_card = {}
    if not is_over then
        room.start_idx = room.next_start_idx or room.start_idx
    end
    return result
end

local function get_next_start_idx( room )
    --get lest zimo pid
    local lest_idx = -1
    local lest_val = 10
    for idx,player in pairs(room.players) do
        LLOG("get_next_start_idx idx: %d pid: %d, zimo: %s", idx, player.id, player.info.zimo )
        if player.info.zimo and lest_val > player.info.zimo then
            lest_val = player.info.zimo
            lest_idx = idx
        end
    end
    if lest_idx ~= -1 then
        room.next_start_idx = lest_idx
        LLOG('111 next_start_idx: %d', lest_idx)
        return
    end

    --get max pao role
    local max_pao_val = 1
    local max_idx = -1
    for idx, p in pairs(room.players) do
        if p.info.is_pao and p.info.is_pao > max_pao_val then
            max_pao_val = p.info.is_pao
            max_idx = idx
        end
    end
    if max_idx ~= -1 then
        room.next_start_idx = max_idx
        LLOG('222 next_start_idx: %d', room.next_start_idx)
        return
    end

    -- get hu but zimo
    local hu_no_zimo_idx = 0
    for idx,p in pairs(room.players) do
        if room.hu_tbl[p.id] and not p.info.zimo then
            hu_no_zimo_idx = idx
            break
        end
    end
    if hu_no_zimo_idx ~= 0 then
        room.next_start_idx = hu_no_zimo_idx
        LLOG('333 next_start_idx: %d', room.next_start_idx )
        return
    end


    -- all fail
    local is_all_same = true
    local last_val = -2000
    for idx, p in pairs(room.players) do
        if last_val == -2000 then
            last_val = p.info.score
        else
            if last_val ~= p.info.score then
                is_all_same = false
                break
            end
        end
    end

    if is_all_same then
        room.next_start_idx = table.index(room.players, room.zhuang)
        LLOG('444 next_start_idx: %d', room.next_start_idx )
        return
    end

    -- lost most idx
    local most_val = 0
    local most_idx = -1

    for idx, p in pairs(room.players) do
        if p.info.score < most_val then
            most_idx = idx
            most_val = p.info.score
        end
    end

    if most_idx ~= -1 then
        room.next_start_idx = most_idx
        return
    end

    room.next_start_idx = table.index(room.players, room.zhuang)
end

local function add_card(player, skip)
    local room = player.room
    local size = #room.cards

    if size == 0 then
        --room.next_start_idx = table.index(room.players, player)
        room:end_game()
        return
    end

    local card
    if table.length( player.info.add_hand ) == 0 and player.info.next_card == -1 then
        card = table.remove(room.cards)
        local card_idx
        for i, v in ipairs(player.info.hand) do
            if v > card then
                card_idx = i
                break
            end
        end
        card_idx = card_idx or #player.info.hand + 1
        table.insert(player.info.hand, card_idx, card)
        player.info.new_idx = card_idx
    elseif table.length( player.info.add_hand ) ~= 0 then
        card = table.remove(player.info.add_hand)
        local card_idx
        for i, v in ipairs(player.info.hand) do
            if v > card then
                card_idx = i
                break
            end
        end
        card_idx = card_idx or #player.info.hand + 1
        table.insert(player.info.hand, card_idx, card)
        player.info.new_idx = card_idx  
    else
        --table.remove_values(room.cards, player.info.next_card)
        card = player.info.next_card
        player.info.next_card = -1
        local card_idx
        for i, v in ipairs(player.info.hand) do
            if v > card then
                card_idx = i
                break
            end
        end
        card_idx = card_idx or #player.info.hand + 1
        table.insert(player.info.hand, card_idx, card)
        player.info.new_idx = card_idx          
    end
    
    if skip then
        LERR("init add_card: %d, pid: %d left_cards: %s", card, player.id, table.dump(room.cards) )
        return
    end
    
    local idx
    for i, role in ipairs(room.players) do
        if role == player then
            idx = i
            role:send(msg.ADD, player.id, card, card_idx)
        else
            role:send(msg.ADD, player.id)
        end
    end

    table.insert(room.playback, table.dump{msg.ADD, player.id, card, card_idx})
    room.can_out = true
    room.idx = idx


    room.out_card = card
    
    LERR("add_card: %d, pid: %d room.hu_tbl: %s left_cards: %s", card, player.id, table.dump(room.hu_tbl), table.dump(room.cards) )
    return card
end

local function add_chk_master_card(player, send_tbl)
    local room = player.room
    LLOG("----add_chk_master_card send_tbl: %s", table.dump(send_tbl) )

    --for i, role in ipairs(room.players) do
    player:send(msg.ZZ_CHK_MASTER, send_tbl )
    --end
    table.insert(room.playback, table.dump{msg.ZZ_CHK_MASTER, send_tbl})

    
    --LERR("add_chk_master_card: %d, pid: %d", card, player.id)
end


MSG_REG[msg.CHOOSE_CARD] = function(player, idx)
    local room = player.room
    if player.info.next_card ~= -1 then 
        return 
    end    
    local card = table.remove(room.cards, idx)
    player.info.next_card = card
end
 
MSG_REG[msg.SHOW_REST_CARDS] = function(player,tp)
    local room = player.room
    if room.is_init_cards == false then
        room.is_init_cards = true
        local cards = {}
        for value = 0, 26 do
            for _ = 1, 4 do
                table.insert(cards, value)
            end
        end     
        --table.random(cards)   
        room.cards = cards
    end
    player:send(msg.SHOW_REST_CARDS, room.cards)
end
 
MSG_REG[msg.CHOOSE_HAND_CARDS] = function(player, add_cards)
    local room = player.room
    
    player.info.add_hand = add_cards
    for _,v in pairs(add_cards) do
        local ret = table.remove_values(room.cards, v)
        if ret == 0 then
            LLOG("CHOOSE_HAND_CARDS failed, fail val: %d, room.cards: %s add_cards: %s", v, tostring( room.cards ), tostring( add_cards ) )
            os.exit()
        end
    end

end

start_room = function(room)
    if not room.is_init_cards then
        local cards = {}
        local total_ct = 0
        for value = 0, 26 do
            for _ = 1, 4 do
                table.insert(cards, value)
                total_ct = total_ct + 1
            end
            if total_ct > 59 then
                --break
            end
        end
        
        table.random(cards)
        room.cards = cards
        room.is_init_cards = true
    end
    LLOG("start_room len: %d cards: %s ", table.length(room.cards), table.dump( room.cards ) )
    
    -- room.cards = table.copy(require "test.card")
    room.idx = room.start_idx
    room.hu_count = nil
    
    local zhuang_player = room.players[room.idx]
    room.zhuang = zhuang_player

    room.sz_val = {math.random(1,6),math.random(1,6)}

    
    for _, player in ipairs(room.players) do
        local player_info = player.info
        player_info.hand = {}
        player_info.extra = {}
        player_info.out = {}
        player_info.select = {}
        player_info.disable_peng = {}
        player_info.guo_pinghu = false
        player_info.guo_dahu = false

        if table.length(player.info.add_hand) ~= 0 then
            for _, v in pairs(player.info.add_hand) do
                table.remove_values( room.cards, v )
            end
        end

        for _ = 1, 13 do
            add_card(player, true)
        end
        
        player_info.new_idx = nil

        
        if player == zhuang_player then
            add_card(player, true)
        end
        
        player.info.add_hand = {}
        player:send(msg.START, player_info.hand, zhuang_player.id, room.sz_val, room.gex_tbl,room.round)
    end
    room.copy_cards = table.copy(room.cards)
    LERR("start_game, room_id: %d zhuang_player.id: %d", room.id, zhuang_player.id)
end


check_master = function(room)

    -- room.cards = table.copy(require "test.card")
    room.start_idx = math.random(1,4)
    --room.start_idx = 1
    room.idx = room.start_idx
    room.hu_count = nil 

    local zhuang_player = room.players[room.idx]
    LLOG("check_master cards: %s start_idx: %d pid: %d", table.dump(cards), room.start_idx, zhuang_player.id )
    room.zhuang = zhuang_player

    local debug_str = ""
    for k,v in pairs(room.players) do
        debug_str = debug_str..", idx:"..k..",pid:"..v.id..";"
    end


    local send_tbl = {}

    local idx =0
    local now_idx = room.idx
    while true do
        local player = room.players[now_idx]
        send_tbl[idx] = player.id
        idx = idx +1
        now_idx = now_idx + 1
        if now_idx > 4 then now_idx = 1 end
        if idx > 3 then
            break
        end
    end
    --[[
    for i=room.idx,room.idx+3 do
        local player = room.players[i%4+1]
        send_tbl[i-room.idx] = player.id
    end
    --]]
    LLOG("debug_str: %s send_tbl: %s", debug_str, table.dump(send_tbl) )
    
    for _, player in ipairs(room.players) do
        local player_info = player.info
        player_info.hand = {}
        player_info.extra = {}
        player_info.out = {}
        player_info.select = {}
        player_info.disable_peng = {}
        player_info.guo_pinghu = false
        player_info.guo_dahu = false
        
        player_info.new_idx = nil

        add_chk_master_card(player, send_tbl)

        --player:send(msg.START, player_info.hand, zhuang_player.id)
    end
    room.is_chk_master = true
    
    --room.can_out = false
    --room:broadcast(msg.START_OUT, zhuang_player.id)
    
    LERR("check_master, room_id: %d", room.id)
end


function get_card_type( card_v )
    return math.floor( card_v / 9 )
end
--[[
function get_card_type(val)
    local limit_ary = {{0,8},{9,17},{18,26}} 
    local now_idx = 0
    for k,v in pairs(limit_ary) do
        if val >= v[1] and val <= v[2] then
            now_idx = k
            break
        end
    end
    return now_idx
end
]]


-- 9->1tiao 18->1tong
function get_card_type_val(val)
    local limit_ary = {{0,8},{9,17},{18,26}} 
    local now_idx = 0
    for k,v in pairs(limit_ary) do
        if val >= v[1] and val <= v[2] then
            now_idx = k
            break
        end
    end
    return val - limit_ary[now_idx][1]
end

local function is_que( val,que_val )
    return get_card_type(val) == que_val
end

function is_player_que(room, player )
    local que_tp = room.que_idx_tbl[player.id]
    for _,card_v in pairs(player.info.hand) do
        if que_tp == get_card_type( card_v ) then
            return true
        end
    end

    return false
end

local function check_next(room, step, force_pao)
    local out_player = room.out_player
    local card = room.out_card

    LLOG("check_next step: %d hu_tbl: %s", step, table.dump(room.hu_tbl) )
    local has_oper = false
    if step == 1  then
        local pao_tbl = {}
        for _, player in ipairs(room.players) do
            LLOG("check_hu pid: %d out_pid: %d hu_tbl: %s que_val: %d out_card: %d", player.id, out_player.id, table.dump(room.hu_tbl), room.que_idx_tbl[player.id], card )
            if player == out_player then
                LLOG("check_hu pid: %d is out_player", player.id)
            end
            if not is_player_que(room, player) then
                LLOG("is_player_que pid: %d is not que hand: %s que_val: %d", player.id, table.dump(player.info.hand), 
                    room.que_idx_tbl[player.id])
            end
            if player ~= out_player and not room.hu_tbl[player.id] and not is_player_que(room,player) then
                local ret = check_hu(room, player.info.hand, card)
                LLOG("check_hu pid: %d hand: %s card: %d ret: %s", player.id, table.dump(player.info.hand), card, tostring(ret) )
                if ret then
                    if ret >= 1 then 
                        player.info.select.hu = true
                        player:send(msg.SELECT, "hu")
                        pao_tbl[player] = ret
                        has_oper = true
                    end
                end
            end
        end   
        if not table.is_empty(pao_tbl) then
            room.pao_tbl = pao_tbl
            --return
        end
    end

    if step <= 2 then
        room.pao_tbl = room.pao_tbl or {}
        for _, player in ipairs(room.players) do
            if ( table.length(room.pao_tbl) ~= 0 and room.pao_tbl[player] ) or (table.length(room.pao_tbl) == 0) then
                LLOG("check_gang pid: %d hand: %s card: %d", player.id, table.dump(player.info.hand), card)
                if is_que(card, room.que_idx_tbl[player.id]) then
                    --empty
                elseif player ~= out_player and card_count(player.info.hand, card) == 3 and not room.hu_tbl[player.id]  then
                  
                    player.info.select.gang = true
                    player:send(msg.SELECT, "gang")
                    
                    if player.info.disable_peng[card] == nil then
                        player.info.select.peng = card
                        player:send(msg.SELECT, "peng")
                    end
                    has_oper = true
                    --return
                end
            end
        end
        
        for _, player in ipairs(room.players) do
            if ( table.length(room.pao_tbl) ~= 0 and room.pao_tbl[player] ) or (table.length(room.pao_tbl) == 0) then
                LLOG("check_peng pid: %d hand: %s card: %d pao_tbl: %s", player.id, 
                    table.dump(player.info.hand or {}), card, table.dump(room.pao_tbl or {}))
                if is_que(card, room.que_idx_tbl[player.id]) then
                    --empty
                elseif player ~= out_player and player.info.disable_peng[card] == nil 
                    and card_count(player.info.hand, card) == 2 and not room.hu_tbl[player.id]  then
                        player.info.select.peng = card
                        player:send(msg.SELECT, "peng")
                        has_oper = true
                        --return
                end
            end
        end
    end

    if has_oper then
        return
    end
    
    local next_idx = room.out_idx
    local next_player = nil
    local safe_ct = 0
    while true do
        next_player = room.players[next_idx]
        if room.hu_tbl[next_player.id] then
            next_idx = get_next_idx(next_idx)
            room.out_idx = next_idx
        else
            break
        end
        safe_ct = safe_ct + 1
        if safe_ct >= 4 then
            LERR("check_next failed, cant find prop player!!!")
            break
        end
    end
    add_card(next_player)
end

local function ROOM_MSG_REG(pt, func)
    MSG_REG[pt] = function(player, ...)
        local room = player.room
        if not room then
            LERR("room msg failed, pt: 0x%08x, not in room, pid: %d", pt, player.id)
            return
        end
        
        local room_type = room.type
        if room_type ~= "hongzhong" and room_type ~= "zhuanzhuan" then
            LERR("room msg failed, pt: 0x%08x, invalid room type: %s, pid: %d", pt, room_type, player.id)
            return
        end
        func(room, player, ...)
    end
end

ROOM_MSG_REG(msg.ZZ_OUT, function(room, player, card)
    if not room.can_out then
        LERR("failed out card, room can not out, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    local out_player = room.players[room.idx]
    if out_player ~= player then
        LERR("failed out card, not in out turn, room_id: %d, pid: %d", room.id, player.id)
        return
    end

    if room.hu_tbl[player.id] then
        LERR("failed out card, already hu, room_id: %d, pid: %d hu_tbl: %s", room.id, player.id, table.dump( room.hu_tbl ) )
        return
    end

    
    if card == nil then
        LERR("invalid out card, pid: %d", player.id)
        return
    end
    
    player.info.is_out = (player.info.is_out or 0) + 1
    if player.info.gsh_cardv ~= 200 then
        room.gsp_cardv = card
    else
        room.gsp_cardv = 200
    end
    player.info.gsh_cardv = 200

    LLOG("ZZ_OUT hand: %s,  card: %d layer.info.gsh_cardv: %d", table.dump(player.info.hand), card, player.info.gsh_cardv )
    --[[
    if player.info.hand[idx] ~= card then
        LERR("failed out card, invalid card: %d, hand: %d, idx: %d, pid: %d", card, player.info.hand[idx], idx, player.id)
        return
    end
    ]]
    idx = table.index( player.info.hand, card )
    
    table.remove(player.info.hand, idx)
    table.insert(player.info.out, card)
    LLOG("ZZ_OUT card: %d idx: %d hand: %s info.out: %s", card, idx, table.dump(player.info.hand), table.dump(player.info.out ) )
    room.can_out = false
    player.info.disable_peng = {}
    player.info.guo_pinghu = false
    player.info.guo_dahu = false
    player.info.new_idx = nil
    room:broadcast(msg.OUT, player.id, card)
    room.pre_out_role = player
    room.out_player = out_player
    room.out_card = card
    room.out_idx = get_next_idx(room.idx)
    check_next(room, 1)
    LLOG("out card success, room_id: %d, pid: %d, card: %d left: %s", room.id, player.id, card, table.dump(player.info.hand) )
end)


ROOM_MSG_REG(msg.ZZ_HU, function(room, player, is_pao)
    local gang_score_count = 0
    if room.type == "zhuanzhuan" then
        for _, v in ipairs(player.info.extra) do
            if v.num == 4 then
                gang_score_count = gang_score_count + 1
            end
        end
    end
    LLOG("called msg.ZZ_HU pid: %d player.hand: %s is_pao: %s", player.id, table.dump(player.info.hand), tostring(is_pao) )
    
    local hu_ret
    if is_pao == nil then
        if not room.can_out then
            LERR("hu failed, can not out, room_id: %d, pid: %d", room.id, player.id)
            return
        end
        
        if room.players[room.idx] ~= player then
            LERR("hu failed, not in turn, room_id: %d, pid: %d", room.id, player.id)
            return
        end
        
        if player.info.new_idx == nil then
            LERR("hu failed, nil new idx, room_id: %d, pid: %d", room.id, player.id)
            return
        end
        
        hu_ret = check_hu(room, player.info.hand)
        if not hu_ret then
            LERR("hu failed, can not hu, room_id: %d, pid: %d", room.id, player.id)
            return
        end
        
        room.can_out = false

        local no_hu_tbl = {}
        for _,p in pairs(room.players) do
            if p.id ~= player.id and not room.hu_card[p.id] then
                table.insert(no_hu_tbl, p)
            end
        end

        room.hu_card[player.id] = {room.out_card,no_hu_tbl}
        local zimo_count = 1
        for _,p in pairs(room.players) do
            if p.info.zimo and p.info.zimo >0 then
                zimo_count = zimo_count + 1
            end
        end
        player.info.zimo = zimo_count
        player.info.hu_type = 0
        if not player.info.is_out == 0 and room.zhuang_player == player then    
            player.info.hu_type = BASE_HU_TYPE.HU_TIANHU --天胡            
        end
        
        if player.info.gsh_cardv == room.out_card then
            player.info.hu_type_gsh = 1
        else
            player.info.hu_type_gsh = 0
        end
        if room.pao_tbl then
            room.pao_tbl[player] = nil
        end
        LLOG("pid: %d gsh_cardv: %d rom.out_card: %d hu_type_gsh: %d", player.id, player.info.gsh_cardv or -100, 
            room.out_card or -1, player.info.hu_type_gsh or -100)
        --room.next_start_idx = room.idx
        room:broadcast(msg.ZZ_HU_RET, player.id, 0)
    else
        local pao_tbl = room.pao_tbl or {}
        hu_ret = pao_tbl[player]
        if not hu_ret then
            LERR("hu failed, zero hu_count, room_id: %d, pid: %d", room.id, player.id)
            return
        end
        
        pao_tbl[player] = nil
        player.info.select = {}
        
        local out_player = room.out_player
        
        if not is_pao then        
            
            if table.is_empty(pao_tbl) then
                if  not room:end_game() then
                    room.out_idx = get_next_idx(room.idx)
                    check_next(room,2)
                end                
            end
            return
        end
        table.insert( player.info.hand, room.out_card )
        room:broadcast(msg.ZZ_HU_RET, player.id, 1)
        for i, v in ipairs(out_player.info.extra) do
            if v.value == room.out_card then
                table.remove(out_player.info.extra, i)
                break
            end
        end
        
        out_player.info.is_pao = (out_player.info.is_pao or 0) + 1
        --room.next_start_idx = table.index(room.players, player)
        room.hu_card[player.id] = {room.out_card, {out_player} }
    end

    LLOG("player info: %s", table.dump(player.info))
    if player.info.hu_type ~= 1 and room.zhuang_player ~= player and player.info.is_out < 1 then    
        player.info.hu_type = BASE_HU_TYPE.HU_DIHU --地胡            
    end

    --杠上炮
    if room.gsp_cardv == room.out_card then
        player.info.hu_type_gsp = 1
    else
        player.info.hu_type_gsp = 0
    end
    if not player.info.hu_type_gsh then
        player.info.hu_type_gsh = 0
    end
    room.hu_tbl[player.id] = 1
    if not room.out_card  or room.out_card == -1 then
        player.info.zimo = 1
    end
    LLOG("pid: %d gsp_cardv: %d rom.out_card: %d hu_type_gsp: %d", player.id, room.gsp_cardv or -1, room.out_card or -1, player.info.hu_type_gsp)
    --[[
    local horse
    if room.horse_type ~= 3 then
        horse = 0
    else
        horse = 1
    end
    
    local horse_tbl = {}
    local size = #room.cards
    
    local horse_size = room.horse or 0
    
    for i = math.max(1, size - horse_size + 1), size do
        local card = room.cards[i]
        table.insert(horse_tbl, card)
        local card_num = card % 9 + 1
        if card_num == 1 or card_num == 5 or card_num == 9 then
            if room.horse_type ~= 3 then
                horse = horse + 1
            else
                horse = horse * 2
            end
            if not player.info.horse_tbl then
                player.info.horse_tbl = {}
            end 
            table.insert(player.info.horse_tbl, card)
        end
    end
    
    room.horse_tbl = horse_tbl
    ]]

    local add_score = (is_pao == nil and 2 or 1) * hu_ret
    --[[
    if room.horse_type ~= 3 then
        add_score = add_score + horse
    else
        add_score = add_score * horse
    end
    ]]
    
    if is_pao == nil then    
        for _, role in ipairs(room.players) do
            role.info.score = role.info.score - add_score - gang_score_count
            player.info.score = player.info.score + add_score + gang_score_count
        end
    else
        local out_player = room.out_player
        
        out_player.info.score = out_player.info.score - add_score - gang_score_count
        out_player.info.result.pao_count = out_player.info.result.pao_count + 1
        
        player.info.score = player.info.score + add_score + gang_score_count
    end
    
    player.info.result.win_count = player.info.result.win_count + 1
    player.info.result.horse_count = player.info.result.horse_count + (horse == 0 and 0 or 1)
    room.hu_count = (room.hu_count or 0) + 1

 
    if not table.is_empty(room.pao_tbl) then
        LLOG("HU success, wait pao, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    LLOG("HU success, room_id: %d, pid: %d", room.id, player.id)
    --room.next_start_idx = table.index(room.players, (room.hu_count == 1 and player) or room.out_player)
    
    
    if not room:end_game() then
        local tmp_idx = table.index(room.players, player)
        room.out_idx = get_next_idx(tmp_idx)
        room.out_card = -1
        check_next(room,2)
    end
    
end)

ROOM_MSG_REG(msg.ZZ_PENG, function(room, player, is_peng)
    local peng_card = player.info.select.peng
    if not peng_card then
        LERR("peng failed, not in select, pid: %d", player.id)
        return
    end
    
    player.info.select = {}
    
    if not is_peng then
        player.info.disable_peng[peng_card] = true
        check_next(room, 3)
        LLOG("peng success, not peng, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    local out_player = room.players[room.idx]
    local card = table.remove(out_player.info.out)
    
    remove_card(player.info.hand, card, 2)
    table.insert(player.info.extra, {value = card, num = 3})
    
    room.can_out = true
    room.idx = table.index(room.players, player)
    room:broadcast(msg.PENG, player.id, out_player.id)
    room.pre_out_role = nil
    LLOG("peng success, room_id: %d, pid: %d", room.id, player.id)
end)

ROOM_MSG_REG(msg.ZZ_GANG, function(room, player, is_gang)
    if not player.info.select.gang then
        LERR("gang failed, not in select, pid: %d", player.id)
        return
    end
    player.info.select = {}
    
    if not is_gang then
        check_next(room, 3)
        LLOG("gang success, not gang, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    room.last_gang_pid = player.id
    local out_player = room.players[room.idx]
    local card = table.remove(out_player.info.out)
    remove_card(player.info.hand, card, 3)
    table.insert(player.info.extra, {value = card, num = 4, gong = true, pid = out_player.id})
    player.info.result.gang_count = player.info.result.gang_count + 1
    
    room:broadcast(msg.GANG, player.id, out_player.id)
    
    player.info.gsh_cardv = add_card(player)
    LLOG("gang success, room_id: %d, pid: %d", room.id, player.id)
end)

ROOM_MSG_REG(msg.ZZ_OUT_GANG, function(room, player, card)
    if not room.can_out then
        LERR("out gang failed, can not out, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    if room.players[room.idx] ~= player then
        LERR("out gang failed, not in turn, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    if card_count(player.info.hand, card) ~= 4 then
        LERR("hand: %s, card: %d ct: %d", player.info.hand, card, card_count(player.info.hand, card) )
        LERR("out gang failed, lack card in hand, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    room.last_gang_pid = player.id
    remove_card(player.info.hand, card, 4)
    table.insert(player.info.extra, {value = card, num = 4})
    --player.info.result.an_gang_count = player.info.result.an_gang_count + 1
    player.info.result.gang_count = player.info.result.gang_count + 1
    
    room:broadcast(msg.OUT_GANG, player.id, card)
    
    player.info.gsh_cardv = add_card(player)
    LERR("out gang success, room_id: %d, pid: %d card: %d", room.id, player.id, card)
end)

ROOM_MSG_REG(msg.ZZ_PENG_GANG, function(room, player, card)
    if not room.can_out then
        LERR("peng gang failed, can not out, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    if room.players[room.idx] ~= player then
        LERR("peng gang failed, not in turn, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    if card_count(player.info.hand, card) ~= 1 then
        LERR("peng gang failed, lack one card in hand, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    local is_find = false
    for _, data in ipairs(player.info.extra) do
        if data.value == card and data.num == 3 then
            data.num = 4
            data.gong = true
            is_find = true
            break
        end
    end
    
    if not is_find then
        LERR("peng gang failed, lack card in extra, room_id: %d, pid: %d", room.id, player.id)
        return
    end
    
    remove_card(player.info.hand, card, 1)
    --player.info.result.gang_count = player.info.result.gang_count + 1
    player.info.result.an_gang_count = player.info.result.an_gang_count + 1
    
    room:broadcast(msg.PENG_GANG, player.id, card)
    
    room.out_player = player
    room.out_card = card
    room.out_idx = room.idx
    room.can_out = false
    room.pre_out_role = nil

    room.last_gang_pid = player.id

    check_next(room, 1, true)
    LERR("peng gang success, room_id: %d, pid: %d", room.id, player.id)
end)

ROOM_MSG_REG(msg.ZZ_QUE, function (room, player, que_idx )
    LLOG("zz_que pid: %d que_idx: %d", player.id, que_idx)
    if que_idx>3 or que_idx<1 then
        LERR("msg.ZZ_QUE error, pid: %d que_idx: %d", player.id, que_idx)
        return
    end
    room:broadcast(msg.ZZ_QUE_RET, player.id)
    
    room.que_idx_tbl[player.id] = que_idx
    if table.length( room.que_idx_tbl ) >= 4 then
        room.can_out = true
        local zhuang_player = room.players[room.idx]
        --room.zhuang = zhuang_player        
        room:broadcast(msg.START_OUT, zhuang_player.id, room.que_idx_tbl)
    end
end)


ROOM_MSG_REG(msg.ZZ_FISH_OVER, function (room, player)
    LLOG("called ZZ_FISH_OVER pid: %d", player.id)
    if not room.fish_over_ct then room.fish_over_ct = 0 end
    room.fish_over_ct = room.fish_over_ct + 1
    if room.fish_over_ct >= 4 then
        room:raw_end_game()

        room.fish_res_tbl = {}
        room.fish_over_ct = 0
    end
end)

ROOM_MSG_REG(msg.ZZ_3CARDS, function(room, player, card_v_tbl)

    room.exg_3cards_tbl[player.id] = table.copy(card_v_tbl)
    room.exg_3cards_idx_tbl[player.room.idx] = table.copy(card_v_tbl)
    room:broadcast(msg.ZZ_3CARDS_RET, player.id)
    LERR("ZZ_3CARDS pid: %d, exg_3cards_tbl: %s exg_3cards_idx_tbl: %s", player.id, table.dump(room.exg_3cards_tbl), table.dump(room.exg_3cards_idx_tbl) )

    if table.length(room.exg_3cards_tbl) == 4 then

        for _,player in pairs(room.players) do
            LLOG("+++ZZ_3CARDS before pid: %d cards: %s", player.id, table.dump( player.info.hand ))
        end
        LLOG("")

        --clear per 3
        for pid, card_tbl in pairs( room.exg_3cards_tbl ) do
            local tmp_player = room:get_player_by_pid(pid)
            for _,val in pairs(card_tbl) do
                remove_card(tmp_player.info.hand, val)
            end
        end
        for _,player in pairs(room.players) do
            LLOG("+++ZZ_3CARDS after remove pid: %d cards: %s", player.id, table.dump( player.info.hand ))
        end
        LLOG("")

        --do exchange
        if room.sz_val[1] == 1 or room.sz_val[1] == 2 then
            --exg next
            local tmp_tbl = nil
            for idx=1,4 do
                local player = room.players[idx]
                local next_idx = idx + 1
                if next_idx > 4 then next_idx = 1 end
                local next_player = room.players[next_idx]

                if not tmp_tbl then
                    tmp_tbl =  table.copy( room.exg_3cards_tbl[next_player.id] )
                    room.exg_3cards_tbl[next_player.id] = table.copy( room.exg_3cards_tbl[player.id] )
                else
                    local now_tmp_tbl =  table.copy( room.exg_3cards_tbl[next_player.id] )
                    room.exg_3cards_tbl[next_player.id] = table.copy( tmp_tbl )
                    tmp_tbl = now_tmp_tbl
                end
                
            end
        elseif room.sz_val[1] == 3 or room.sz_val[1] == 4 then
            -- exg opsite
            for idx = 1,2 do
                local player = room.players[idx]
                local next_idx = idx + 2
                local next_player = room.players[next_idx]

                local tmp_tbl = table.copy( room.exg_3cards_tbl[player.id] )
                room.exg_3cards_tbl[player.id] = table.copy( room.exg_3cards_tbl[next_player.id] )
                room.exg_3cards_tbl[next_player.id] = tmp_tbl
            end
        else
            -- exg last

            local tmp_tbl = nil
            for idx=1,4 do
                local player = room.players[idx]
                local next_idx = idx - 1
                if next_idx <=0 then next_idx = 4 end
                local next_player = room.players[next_idx]
                LLOG("exg idx: %d with next_idx: %d", idx, next_idx )

                if not tmp_tbl then
                    tmp_tbl =  table.copy( room.exg_3cards_tbl[player.id] )
                    room.exg_3cards_tbl[player.id] = table.copy( room.exg_3cards_tbl[next_player.id] )
                else
                    local now_tmp_tbl =  table.copy( room.exg_3cards_tbl[player.id] )
                    room.exg_3cards_tbl[player.id] = table.copy( tmp_tbl )
                    tmp_tbl = now_tmp_tbl
                end

            end
        end

       for _,player in pairs(room.players) do
            LLOG("+++ZZ_3CARDS before merge pid: %d cards: %s", player.id, table.dump( player.info.hand ))
        end
        LLOG("")

        LLOG("exg_3cards_tbl: %s ", table.dump(room.exg_3cards_tbl) )
        for id, card_tbl in pairs( room.exg_3cards_tbl ) do
            local tmp_player = room:get_player_by_pid( id )
            for _,val in pairs(card_tbl) do
                table.insert( tmp_player.info.hand, val)
            end
            table.sort( tmp_player.info.hand )
            tmp_player:send(msg.OUT_EXG_CARD, card_tbl, room.sz_val[1])
        end
        for _,player in pairs(room.players) do
            LLOG("+++ZZ_3CARDS after pid: %d cards: %s", player.id, table.dump( player.info.hand ))
        end
    end
end)

return {
    BASE_ROUND = 4,
    create_room = create_room,
    create_info = create_info,
    renter = renter,
    get_result = get_result,
    get_next_start_idx = get_next_start_idx,
    get_card_type_val = get_card_type_val,
    get_card_type = get_card_type,
    get_next_idx = get_next_idx,
    start_room = start_room,
    check_master = check_master,
    set_init_msg = set_init_msg,
}
